/**
 * Created by Vasil Toshev on 09.11.13.
 */
/* global ServerController: true, d3, LinkRegister, myAppViewModel, _, Timer, $  */
'use strict';
var comparisonPopupArray = [];



// Settings
var defaultRadius = 45; //default node radius in pixels
var dropTargetSize = defaultRadius * 2;

var myServerController = new ServerController();
var width = getDivD3Size().width;
var height = getDivD3Size().height;

//this maps and normalizes the link relevance to various link properties
var linkDistanceScale = d3.scale.linear()
    .domain([5, 20])
    .range([100, 0]);
var linkStrengthScale = d3.scale.linear()
    .domain([5, 20])
    .range([0.04, 0.08]);
var linkStrokeScale = d3.scale.linear()
    .domain([5, 25])
    .range([-1, 4]);

var register = new LinkRegister(d3.layout.force(), [], [], null);
var force = register.force
    .charge(-100)//current -100 //function(d, i) { return -d[selectedAttr]}
    .linkDistance(function(d){ return defaultRadius*2+linkDistanceScale(d.intersection.relevance); })//current 150
    .linkStrength(function(d){ return linkStrengthScale(d.intersection.relevance); })//[0-1] default 1 current 0.1
    .friction(0.9)//[0-1] default 0.9
    .gravity(0.015)//[0-...] default 0.1 current 0.01
    .size([width, height])
    .on("tick", tick);
register.init();

var fill = d3.scale.category10();

var groupFill = function(d, i) { return fill(i); };
var groups = [];

var groupPath = function(d) {
    return "M" +
        d3.geom.hull(d.values.map(function(i) { return [i.x, i.y]; }))
            .join("L") + "Z";
};

var svg = d3.select("#divD3").append("svg")
    .attr("display","block")
    .attr({"width": "100%","height": "100%"})
;

//create a clipPath for the images
svg.append("svg:clipPath")
    .attr("id", "circleClipPath")
    .append("svg:circle")
    .attr("r", defaultRadius);
//create 2 groups for z-order. Links should always be underneath nodes
svg.append("g").attr("id","links");
svg.append("g").attr("id","nodes");
var link = svg.select("#links").selectAll(".link");
var node = svg.select("#nodes").selectAll(".node");

function redraw() {
    //LINK UPDATE
    link = link.data(force.links(), function(d) { return d.source.resource.id + "-" + d.target.resource.id; });
    link
        .attr("stroke",function(d){return d3.rgb("#5b7876").hsl().brighter(linkStrokeScale(d.intersection.relevance)); });

    //LINK ENTER
    link.enter().insert("line", ".node")
        .attr("class", "link")
        .attr("stroke",function(d){return d3.rgb("#5b7876").hsl().brighter(linkStrokeScale(d.intersection.relevance)); })
        .style("stroke-width",4);
        //.style("stroke-width",function(d){return linkStrokeWidthScale(d.intersection.relevance)})//currently 4

    //LINK EXIT
    link.exit().remove();

    //NODE UPDATE
   node = node.data(force.nodes(), function(d) { return d.resource.id;});

    //NODE ENTER
    var gEnter = node.enter().append("g")
        .attr("class","gEnter")
        .call(customDrag)
        .call(d3.behavior.zoom().scaleExtent([1, 2]).on("zoom", onZoom))
        .on("dblclick.zoom", onDoubleClickZoom)
        .on("click",onClick)
    ;
    gEnter
        .attr("opacity","0")
        .transition()
        .duration(1500)
        .attr("opacity", "1")
    ;
    var nodeG = gEnter.append("g")
        .attr("class","nodeG")
        .attr("clip-path","url(#circleClipPath)")
    ;
    var outerSVG = nodeG.append("svg")
        .attr("class","outerSVG")
        .attr("width",defaultRadius*4+"px")
        .attr("height",defaultRadius*5+"px")
        .attr("x","-"+defaultRadius*2+"px")
        .attr("y","-"+defaultRadius*1.5+"px")
    ;
    var outerRect = outerSVG.append("svg:rect")
        .attr("class","outerRect")
        .attr("width","100%")
        .attr("height","100%")
        .attr("fill","#8f9e8b")
    ;
    var innerTopSVG = outerSVG.append("svg")
        .attr("class","innerTopSVG")
        .attr("width","100%")
        .attr("height",defaultRadius*3+"px")
    ;
    var innerTopRect = innerTopSVG.append("svg:rect")
        .attr("class","innerTopRect")
        .attr("width","100%")
        .attr("height","100%")
        .attr("fill","#000000")
    ;
    var innerTopImage = innerTopSVG.append("image")
        .attr("class","innerTopImage")
        .attr("xlink:href", function(d) {
            return getImageLocation(d);
        })
        .attr("width", "100%")
        .attr("height", "100%")
    ;
    var innerTopVideoIcon = innerTopSVG.append("image")
        .attr("class","innerTopVideoIcon")
        .attr("xlink:href", function(d) {
            if(d.resource.video){
                //display a video icon
                return "img/icon-playIcon.png";
            }else {
                return null;
            }
        })
        .attr("width", "100%")
        .attr("height", "100%")
    ;

    var innerBottomSVG = outerSVG.append("svg")
        .attr("class","innerBottomSVG")
        .attr("display","none")
        .attr("y",defaultRadius*3+"px")
        .attr("width","100%")
        .attr("height",defaultRadius*2+"px")
    ;
    var innerBottomForeignObject = innerBottomSVG.append("foreignObject")
        .attr("class","innerBottomForeignObject")
        .attr("width","100%")
        .attr("height","100%")
        .append("xhtml:div")
            .attr("class","expandedNodeTextDiv")
            .html(function(d){return "<div><span class=\"expandedTitle\">"+ d.resource.title+" (</span>"+ d.resource.creator+")</div><p>Type: "+ d.resource.type+"</p><p>Medium: "+d.resource.medium+"</p><p>Association: "+customArrToStr(d.resource.association)+"</p>"+"</p><p>Subject: "+customArrToStr(d.resource.subject)+"</p>"+"</p><p>Atmosphere: "+customArrToStr(d.resource.atmosphere)+"</p>"+"</p><p>Emotion: "+customArrToStr(d.resource.emotion)+"</p>";})
    ;
    var uiSVG = gEnter.append("g")
        .attr("class","uiSVG");
    var pinCircle = uiSVG.append("svg:circle")
        .attr("class","pinCircle")
        .attr("cx","0")
        .attr("cy","0")
        .attr("r", defaultRadius+2)
        .attr("opacity","0")
        .attr("fill","none")
        .attr("stroke","red")
        .attr("stroke-width","4")
    ;
    var titleForeignObject = uiSVG.append("foreignObject")
        .attr("class","titleForeignObject")
        .attr("width",defaultRadius*6)
        .attr("height","200")
        .attr("x",-(defaultRadius*3))
        .attr("y",defaultRadius+5)
        .attr("display","none")
        .append("xhtml:div")
            .attr("class","titleDiv")
            .html(function(d){return d.resource.title;})
    ;

    //NODE EXIT
    node.exit()
        .transition()
        .duration(100)
        .attr("opacity", "0")
        .remove()
    ;
    force.start();
}
function getImageLocation(d){
    if(d.resource.image){
        //resource is an image
        if (d.resource.image.substring(0, 4) === "http") {
            return d.resource.image;
        } else {
            return "http://193.26.192.141/"+d.resource.thumbnail;
        }
    } else if(d.resource.video){
        //resource is a video
        var tempYouTubeID = d.resource.video.split('embed/')[1];
        //display a pregenerated youtube thumbnail
        return "http://img.youtube.com/vi/"+tempYouTubeID+"/1.jpg";
    } else {
        console.log("image not found: "+ d.resource.image +", "+ d.resource.video);
        return "./img/img-not-found.png";
    }
}
function tick(e) {
    //use gravity
//    node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
//    link.attr("x1", function(d) { return d.source.x; })
//        .attr("y1", function(d) { return d.source.y; })
//        .attr("x2", function(d) { return d.target.x; })
//        .attr("y2", function(d) { return d.target.y; });

//CLUSTERING
//TODO: optimize by using groups?
    if (myAppViewModel.enableClustering()) {
        if (myAppViewModel.selectedGroupKey() !== "") {
            node.each(cluster(force.alpha() * force.alpha()));//maybe multiply by 10?
        }
    }
    if (myAppViewModel.enableCollision()) {
        node.each(collide(0.5));
    }
    node.each(collideForComparison());

    //use bounding box
    node.attr("transform", function(d) { return "translate(" + Math.max(defaultRadius, Math.min(width - defaultRadius, d.x)) + "," + Math.max(defaultRadius, Math.min(height - defaultRadius, d.y)) + ")"; });
    link.attr("x1", function(d) { return Math.max(defaultRadius, Math.min(width - defaultRadius, d.source.x)); })
        .attr("y1", function(d) { return Math.max(defaultRadius, Math.min(width - defaultRadius, d.source.y)); })
        .attr("x2", function(d) { return Math.max(defaultRadius, Math.min(width - defaultRadius, d.target.x)); })
        .attr("y2", function(d) { return Math.max(defaultRadius, Math.min(width - defaultRadius, d.target.y)); });

    var convexHullSVG = svg.selectAll("path")
        .data(groups)
            .attr("d", groupPath);
    convexHullSVG.enter().insert("path", "g")
            .style("fill", groupFill)
            .style("stroke", groupFill)
            .style("stroke-width", 100)
            .style("stroke-linejoin", "round")
            .style("opacity", 0.2)
            .attr("d", groupPath);
    convexHullSVG.exit().remove();
}
//CLUSTERING
// Move d to be adjacent to the cluster node.
function cluster(alpha) {
    return function(d) {
        var tempKey = d.resource[myAppViewModel.selectedGroupKey()];
        var tempGroup = _.find(groups, function(obj) {return (obj.key === tempKey) && (obj.values.length>2);});
        if(tempGroup){
            var cluster = tempGroup.values[0];//get the first element in the group
            if (cluster === d) {return;}
            var x = d.x - cluster.x,
                y = d.y - cluster.y,
                l = Math.sqrt(x * x + y * y),
                r = defaultRadius*2;//d.r + cluster.r;
            if (l !== r) {
                l = (l - r) / l * alpha;
                d.x -= x *= l;
                d.y -= y *= l;
                cluster.x += x;
                cluster.y += y;
            }
        }
    };
}
// Resolves collisions between d and all other circles.
function collide(alpha) {
    var quadtree = d3.geom.quadtree(register.nodes);
    return function(d) {
        var r = defaultRadius,//d.radius + maxRadius + Math.max(padding, clusterPadding),
            nx1 = d.x - r,
            nx2 = d.x + r,
            ny1 = d.y - r,
            ny2 = d.y + r;
        quadtree.visit(function(quad, x1, y1, x2, y2) {
            if (quad.point && (quad.point !== d) ) {
                var x = d.x - quad.point.x,
                    y = d.y - quad.point.y,
                    l = Math.sqrt(x * x + y * y),
                    r = defaultRadius*2; //+ quad.point.radius;// + (d.cluster === quad.point.cluster ? padding : clusterPadding);
                if (l < r) {
                    l = (l - r) / l * alpha;
                    d.x -= x *= l;
                    d.y -= y *= l;
                    quad.point.x += x;
                    quad.point.y += y;
                }
            }
            return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
        });
    };
}
function collideForComparison() {
    var quadtree = d3.geom.quadtree(register.nodes);
    return function(d) {
        var r = defaultRadius,//d.radius + maxRadius + Math.max(padding, clusterPadding),
        nx1 = d.x - r,
        nx2 = d.x + r,
        ny1 = d.y - r,
        ny2 = d.y + r;
        quadtree.visit(function(quad, x1, y1, x2, y2) {
            if (quad.point && (quad.point !== d) && quad.point.dragged && d.dragged ) {
                var x = d.x - quad.point.x,
                    y = d.y - quad.point.y,
                    l = Math.sqrt(x * x + y * y),
                    r = defaultRadius*2+4; //collision will happen with 4 pixels padding
                //if nodes are colliding
                if (l < r) {
                    //if they are not currently being compared
                    if(!quad.point.compared && !d.compared){
                        quad.point.compared = d.compared = true;
                        checkForComparisonPopup(quad.point,d);
                    }
                } else {
                    quad.point.compared = d.compared = false;
                }
            }
        });
    };
}
function checkForComparisonPopup(d1,d2){
    //check if popup already exists in the popup array
    var alreadyExists = false;
    for(var i=0;i<comparisonPopupArray.length;i++){
        if(comparisonPopupArray[i]===d1.resource.id.concat(d2.resource.id) || comparisonPopupArray[i]===d2.resource.id.concat(d1.resource.id)){
            alreadyExists = true;
            return;
        }
    }
    if(!alreadyExists){
        createAComparisonPopup(d1,d2);
    }
}
function createAComparisonPopup(d1,d2){
    var tempIntersection = getNodesIntersection(d1,d2,true);
    if(tempIntersection){
        //initialize a popup
        var myPopup = d3.select("body").append("div")
            .attr("class","comparisonPopup")
            .style("position", "absolute")
            .style("z-index", "10")
            .style("top", 0.5*(d1.y+d2.y)-100+"px").style("left",0.5*(d1.x+d2.x)-150+"px")
            .html(
                "<span class=\"closeButton\">x</span>" +
                "<img class=\"firstImage\" src=\""+getImageLocation(d1)+"\"></img>" +
                "<img class=\"secondImage\" src=\""+getImageLocation(d2)+"\"></img>" +
                "<span class=\"fieldSpan\">association: " +
                    "<span class=\"arraySpan\">"+customArrToStr(tempIntersection.association)+"</span>" +
                " </span>" +
                "<span class=\"fieldSpan\">subject: " +
                    "<span class=\"arraySpan\">"+customArrToStr(tempIntersection.subject)+"</span>" +
                " </span>" +
                "<span class=\"fieldSpan\">atmosphere: " +
                    "<span class=\"arraySpan\">"+customArrToStr(tempIntersection.atmosphere)+"</span>" +
                " </span>" +
                "<span class=\"fieldSpan\">emotion: " +
                    "<span class=\"arraySpan\">"+customArrToStr(tempIntersection.emotion)+"</span>" +
                " </span>" +
                "<span class=\"fieldSpan\">creator: " +
                    "<span class=\"arraySpan\">"+customArrToStr(tempIntersection.creator)+"</span>" +
                "</span>"
            )
            .on("click", function() {
                //remove the popup and delete it from the popup array
                myPopup.remove();
                d3.event.preventDefault();
                comparisonPopupArray = _.without(comparisonPopupArray,d1.resource.id.concat(d2.resource.id));
                comparisonPopupArray = _.without(comparisonPopupArray,d2.resource.id.concat(d1.resource.id));
            })
            ;
        myPopup
            .style("opacity","0")
            .transition()
            .duration(500)
            .style("opacity","1")
        ;
        comparisonPopupArray.push(d1.resource.id.concat(d2.resource.id));
    }
}

function customArrToStr(a){
    var tempString = a.join(", ");
    tempString = tempString ? tempString : "-";
    return tempString;
}
function getFirstNodeByAttr(attr){
    for(var i=0;i<groups.length;i++){
        if(attr===groups[i].key){
            return groups[i].values[0];
        } else {return null;}
    }
}
var myTimer = new Timer();
myTimer.Interval = 8000;
myTimer.Tick = appTimerTick;

function appTimerTick(){
    //select a random action
    var actionArray = ["switchNode","pinRandomNode"];
    var tempAction = actionArray[Math.floor(Math.random()*actionArray.length)];
    switch(tempAction)
    {
        //keep nodes count between 25 and 12
        case "switchNode":
            if(register.nodes.length>25){
                screensaverRemoveNode();
            } else if (register.nodes.length<12){
                screensaverAddNode();
            } else {
                if(Math.random()<0.5){
                    screensaverRemoveNode();
                }else{
                    screensaverAddNode();
                }
            }
            break;
        case "pinRandomNode":
            screensaverPinRandomNode();
            break;
        default:
            //do nothing
    }
}
function screensaverPinRandomNode(){
    var randomIndex = Math.floor(Math.random()*register.nodes.length);
    var tempNode = d3.selectAll(".gEnter").filter(function(d, i) { return i===randomIndex; });
    if(!tempNode.datum().pinned){
        pinNode(tempNode);
    } else {
        unpinNode(tempNode);
    }
}
function screensaverRemoveNode(){
    //sort nodes by attention (use +obj.attention to make sure it's interpreted as a number)
    var sortedArray = _.sortBy(register.nodes, function(obj){ return +obj.attention;});
    //remove the node with lowest attention
    var tempNode = sortedArray.slice(0,Math.min(sortedArray.length,1));
    if(tempNode){
        removeNodeAndAllLinks(tempNode);
    }
    redraw();
    calculateLinks();
}
function screensaverAddNode(){
    var sortedArray = _.sortBy(register.nodes, function(obj){ return +obj.attention;});
    //get the 3 nodes with highest attention
    sortedArray = sortedArray.slice(Math.max(sortedArray.length-3,0));
    //get the 3 most sought keywords within these nodes
    var mostSoughtKeywordsArray = [];
    _.each(sortedArray,function (obj){
        mostSoughtKeywordsArray = mostSoughtKeywordsArray.concat(obj.resource.association).concat(obj.resource.subject).concat(obj.resource.atmosphere).concat(obj.resource.emotion);
    });
    mostSoughtKeywordsArray = _.countBy(mostSoughtKeywordsArray);
    mostSoughtKeywordsArray = _.pairs(mostSoughtKeywordsArray);
    mostSoughtKeywordsArray = _.sortBy(mostSoughtKeywordsArray,function(obj){ return obj[1];});
    mostSoughtKeywordsArray = mostSoughtKeywordsArray.slice(Math.max(mostSoughtKeywordsArray.length-3,0));
    //add the most relevant resource for these keywords
    var tempSearchString = "";
    _.each(mostSoughtKeywordsArray, function (obj){
        tempSearchString=tempSearchString.concat(obj[0]) + " OR ";
    });
    tempSearchString = tempSearchString.slice(0, -Math.max(" OR ".length,0));
    findByText(tempSearchString,1);
    //reduce attention by >10% each tick
    _.each(register.nodes, function (node){
        if(node.attention>0){
            node.attention = Math.floor(node.attention*0.9);
        }
    });
    redraw();
    calculateLinks();
}
var customDrag = force.drag()
        .on("dragstart", dragstart)
        .on("drag", drag)
        .on("dragend",dragend)
    ;
function dragstart(d) {
    d3.event.sourceEvent.stopPropagation();
    //bring the node to top
    this.parentNode.appendChild(this);
    //add attention to the node
    d.attention+=1;
    d.dragged=true;

    //show title while dragging
    if(!d.expanded && !d.pinned){
        showTitle(d3.select(this));
    }
}
function drag(d){
    tick();
}
function dragend(d) {
    d.dragged=false;
    //hide title when no longer dragging
    if(!d.expanded && !d.pinned){
        hideTitle(d3.select(this));
    }

    //if node is dragged to the dropTargets
    if (d.y > height - dropTargetSize) {//bottom
//        if (d.x > width - dropTargetSize) {//bottom right
//            if(d.resource.video){
//                playVideo(d.resource.video);
//            }
//        } else
        if (d.x < dropTargetSize){//bottom left - recycle bing
            removeNodeAndAllLinks(d);
        }
    }
    redraw();
    calculateLinks();
}
function removeNodeAndAllLinks(d){
    for(var i=0;i<register.nodes.length;i++){
        register.orphan_node(register.nodes[i]);
    }
    register.remove_node(d);
    updateGroups();
}
function playVideo(v){
    $.magnificPopup.open({
        disableOn: 700,
        items: {
            src: v,
            type: 'iframe'
        },
        mainClass: 'mfp-fade',
        removalDelay: 160,
        preloader: false,
        fixedContentPos: false,
        //pauses the force while video is playing
        callbacks: {
            open: function() {
                force.stop();
            },
            close: function() {
                force.resume();
            }
        }
    });
}
function onZoom(d){
    var tempNode = d3.select(this);
    //scale must be between 1 and 2
    var tempScale = d3.event.scale;
    tempNode.select(".nodeG")
        .attr("transform", "scale("+tempScale+")")
    ;
    tempNode.select(".pinCircle")
        .attr("r", defaultRadius*tempScale+2)
    ;
    if(tempScale>1.5){
        expandNode(tempNode);
    } else{
        contractNode(tempNode);
    }
}
function onDoubleClickZoom(d){
    var tempNode = d3.select(this);
    if(!d.pinned){
        pinNode(tempNode);
    } else {
        unpinNode(tempNode);
    }
}
function onClick(d){
    if(d.resource.video){
        playVideo(d.resource.video);
    }
}

function expandNode(tempNode){
    if(!tempNode.datum().expanded){
        tempNode.datum().attention+=1;
        tempNode.datum().expanded = true;
        tempNode.select(".nodeG")
            .attr("clip-path","")
        ;
        tempNode.select(".innerBottomSVG")
            .attr("display","block")
        ;
        var tempUI = tempNode.select(".uiSVG")
            .attr("opacity","0")
        ;
        tempNode.datum().fixed = 7;//bitwise 111
        tempNode.datum().px = tempNode.datum().x;
        tempNode.datum().py = tempNode.datum().y;
        redraw();
    }
}
function contractNode(tempNode){
    if(tempNode.datum().expanded){
        tempNode.datum().expanded = false;
        tempNode.select(".nodeG")
            .attr("clip-path","url(#circleClipPath)")
        ;
        tempNode.select(".uiSVG")
            .attr("opacity","1")
        ;
        if(!tempNode.datum().pinned){
            tempNode.datum().fixed = 6;//bitwise 110
            tempNode.datum().px = tempNode.datum().x;
            tempNode.datum().py = tempNode.datum().y;
        }
        redraw();
    }
}
function pinNode(tempNode){
    var pinIndicator = tempNode.select(".pinCircle");
    pinIndicator
        .attr("opacity",1)
    ;
    showTitle(tempNode);
    tempNode.datum().attention+=1;
    tempNode.datum().fixed = true;
    tempNode.datum().pinned = true;
    calculateLinks();
}
function unpinNode(tempNode){
    var pinIndicator = tempNode.select(".pinCircle");
    pinIndicator
        .attr("opacity",0)
    ;
    hideTitle(tempNode);
    tempNode.datum().fixed = false;
    tempNode.datum().pinned = false;
    calculateLinks();
}

function calculateLinks(){
    //remove all links
    for(var i=0;i<register.nodes.length;i++){
        register.orphan_node(register.nodes[i]);
    }
    //recreate all links
    for(var k=0;k<register.nodes.length;k++){
        if(register.nodes[k].pinned){
            compareNodeToAllExisting(register.nodes[k]);
        }
    }
    redraw();
}

function updateGroups(){
    if(myAppViewModel){
        if(myAppViewModel.selectedGroupKey()!==""){
            getGroupsRollup();
            getGroups();
            redraw();
        } else {
            groups=[];
            myAppViewModel.groupsRollup(groups);
        }
    }
}
function getGroupsRollup(){
    //Get a rollup and update the UI
    var tempGroupsRollup = d3.nest().key(function(d) {return d.resource[myAppViewModel.selectedGroupKey()];})//d.resource.creator
        .rollup(function(leaves) { return leaves.length; })
        .entries(register.nodes)
    ;
    //iterate in reverse to remove elements
    for(var i=tempGroupsRollup.length-1;i>=0;i--){
        //require a defined key and at least 3 nodes to create a group
        if(tempGroupsRollup[i].key==="undefined" || tempGroupsRollup[i].values<3){
            tempGroupsRollup.splice(i,1);
        }
    }
    myAppViewModel.groupsRollup(tempGroupsRollup);
}
function getGroups(){
    //create the groups that the convex hull uses in tick()
    groups = d3.nest().key(function(d) {return d.resource[myAppViewModel.selectedGroupKey()];}).entries(register.nodes);
    //iterate in reverse to remove elements
    for(var i=groups.length-1;i>=0;i--){
        //require a defined key and at least 3 nodes to create a group
        if(groups[i].key==="undefined" || groups[i].values.length<3){
            groups.splice(i,1);
        }
    }
}

function showTitle(tempNode){
    tempNode.select(".titleForeignObject")
        .attr("display","block")
    ;
}
function hideTitle(tempNode){
    tempNode.select(".titleForeignObject")
        .attr("display","none")
    ;
}
function findByText(st,resourcesToDisplay){
    var searchText = st; //possibly clean up the string here(remove all non-alphanumeric characters)
    myServerController.findByText(searchText, function (data) {
        console.log("Search string: \""+ st + "\" ("+data.resources.length+" resources found)");
        var counter = 0;
        var maxNumberResources = resourcesToDisplay ? resourcesToDisplay : 10;
        _.each(data.resources, function (resource) {
            //only add a node if it doesn't already exist
            var matchFound = false;
            _.each(register.nodes, function (node){
                if (resource.id===node.resource.id){
                    matchFound = true;
                    return;
                }
            });
            //if the node hasn't been added yet
            if(!matchFound && counter<maxNumberResources){
                //create an empty JS Object to hold the museum resource, as well as meta data
                var tempData = {};
                tempData.resource = resource;
                tempData.attention = 0;
                tempData.dragged = false;
                tempData.expanded = false;
                tempData.pinned = false;
                tempData.compared = false;
                register.add_node(tempData);
                ++counter;
                redraw();
            }
        });
        updateGroups();
    });
}
function getNodesBySameArtist(d){
    var tempNodeArr = [];
    _.each(register.nodes, function (node){
        if(d!==node){
            if(JSON.stringify(d.resource.creator)===JSON.stringify(node.resource.creator)){
                tempNodeArr.push(node);
            }
        }
    });
    return tempNodeArr;
}
function compareNodeToAllExisting(d){
    for(var i=0;i<register.nodes.length;i++){
        //if the elements are not the same
        if(d.index!==register.nodes[i].index){
            var tempIntersection = getNodesIntersection(d,register.nodes[i]);
            if(tempIntersection){
                register.add_link({"source":d,"target":register.nodes[i],"intersection":tempIntersection});
                //redraw();
            }
        }
    }
}
function debug_createWeb(){
    for(var i=0;i<register.nodes.length;i++){
        for(var k=i+1;k<register.nodes.length;k++){
            var tempIntersection = getNodesIntersection(register.nodes[k],register.nodes[i]);
            if(tempIntersection){
                register.add_link({"source":register.nodes[k],"target":register.nodes[i],"intersection":tempIntersection});
            }
        }
    }
    redraw();
}
function getNodesIntersection(n1,n2,ignoreRelevanceFilter){
    var tempIntersection = {};

    tempIntersection.association = _.intersection(n1.resource.association, n2.resource.association);
    tempIntersection.subject = _.intersection(n1.resource.subject, n2.resource.subject);
    tempIntersection.atmosphere = _.intersection(n1.resource.atmosphere, n2.resource.atmosphere);
    tempIntersection.emotion = _.intersection(n1.resource.emotion, n2.resource.emotion);
    tempIntersection.mainMotive = _.intersection(n1.resource.mainMotive, n2.resource.mainMotive);
    tempIntersection.creator = _.intersection(n1.resource.creator, n2.resource.creator);

    tempIntersection.relevance = 0;
    tempIntersection.relevance += myAppViewModel.relevanceFlagAssociation() ? 1*(tempIntersection.association.length) : 0;
    tempIntersection.relevance += myAppViewModel.relevanceFlagSubject() ? 1*(tempIntersection.subject.length) : 0;
    tempIntersection.relevance += myAppViewModel.relevanceFlagAtmosphere() ? 1*(tempIntersection.atmosphere.length) : 0;
    tempIntersection.relevance += myAppViewModel.relevanceFlagEmotion() ? 1*(tempIntersection.emotion.length) : 0;
    tempIntersection.relevance += myAppViewModel.relevanceFlagMainMotive() ? 3*(tempIntersection.mainMotive.length) : 0;
    tempIntersection.relevance += myAppViewModel.relevanceFlagCreator() ? 3*(tempIntersection.creator.length) : 0;
    //ignoreRelevanceFilter is true when comparing nodes
    if(ignoreRelevanceFilter || tempIntersection.relevance>myAppViewModel.relevanceThreshold()-1){
        return tempIntersection;
    }
    else {return null;}
}

function getDivD3Size(){
    var size = {width: $('#divD3').width()-$('#divUI').width(), height: $('#divD3').height()};
    return (size);
}
window.onresize = function() {
    width = getDivD3Size().width;
    height = getDivD3Size().height;

    force
        .size([width,height]);
};
window.oncontextmenu = function(event) {
    event.preventDefault();
    event.stopPropagation();
    return false;
};